package com.media.selector.media.model

import android.content.Context
import android.content.ContextWrapper
import android.content.res.Resources
import android.os.Bundle
import androidx.annotation.Nullable
import com.media.selector.R
import com.media.selector.media.model.entity.MediaEntity

/**
 * date        ：2020/5/12
 * author      ：蒙景博
 * description ：
 */
class SelectedCollection (context: Context): ContextWrapper(context) {

    companion object {
        const val STATE_SELECTION = "state_selection"
        const val STATE_ALL_DATA = "state_all_data"
        const val STATE_COLLECTION_TYPE = "state_collection_type"

        /**
         * Empty collection
         */
        const val COLLECTION_UNDEFINED = 0x00

        /**
         *  Images collection
         */
        const val COLLECTION_IMAGE = 0x01

        /**
         * videos collection
         */
        const val COLLECTION_VIDEO = 0x01 shl 1

        /**
         * Collection with images and videos.
         */
        const val COLLECTION_MIXED = COLLECTION_IMAGE or COLLECTION_VIDEO
    }

    private var mItems: LinkedHashSet<MediaEntity>? = null
    private var mCollectionType: Int = COLLECTION_UNDEFINED

    fun onCreate(savedInstanceState: Bundle?) {
        if (savedInstanceState == null) {
            mItems = LinkedHashSet()
        } else {
            val mMedias = savedInstanceState.getParcelableArrayList(STATE_SELECTION) ?: listOf<MediaEntity>()
            mItems = LinkedHashSet(mMedias)
            mCollectionType = savedInstanceState.getInt(STATE_COLLECTION_TYPE, COLLECTION_UNDEFINED)
        }
    }

    fun onSaveInstanceState(outState: Bundle) {
        outState.putParcelableArrayList(STATE_SELECTION, mItems?.let { ArrayList(it) })
        outState.putInt(STATE_COLLECTION_TYPE, mCollectionType)
    }

    /**
     * 将选中的媒体数据组装到 Bundle 中并返回
     */
    fun getSelectedDataBundle(): Bundle {
        return Bundle().apply {
            putParcelableArrayList(STATE_SELECTION, mItems?.let { ArrayList(it) })
            putInt(STATE_COLLECTION_TYPE, mCollectionType)
        }
    }

    /**
     * 当前文件夹下所有的媒体数据
     */
    fun getCurrentAlbumAllDataBundle(medias: ArrayList<MediaEntity>): Bundle {
        return Bundle().apply {
            putParcelableArrayList(STATE_ALL_DATA, medias)
        }
    }

    fun getSelectedMediaSize() = mItems?.size ?: 0

    /**
     * 将选中的媒体数据添加到集合存储起来
     */
    fun addMedia(media: MediaEntity): Boolean {
        val added = mItems?.add(media) ?: false
        if (added) {
            if (mCollectionType == COLLECTION_UNDEFINED) {
                if (media.isImage()) {
                    mCollectionType = COLLECTION_IMAGE
                } else if (media.isVideo()) {
                    mCollectionType = COLLECTION_VIDEO
                }
            } else if (mCollectionType == COLLECTION_IMAGE) {
                if (media.isVideo()) {
                    mCollectionType = COLLECTION_MIXED
                }
            } else if (mCollectionType == COLLECTION_VIDEO) {
                if (media.isImage()) {
                    mCollectionType = COLLECTION_MIXED
                }
            }
        }
        return added
    }

    /**
     * 从存储的集合中删除选中的媒体数据
     */
    fun removeMedia(media: MediaEntity): Boolean {
        val removed = mItems?.remove(media) ?: false
        if (removed) {
            if (mItems?.size == 0) {
                mCollectionType = COLLECTION_UNDEFINED
            } else {
                if (mCollectionType == COLLECTION_MIXED) {
                    refineCollectionType()
                }
            }
        }
        return removed
    }

    private fun refineCollectionType() {
        var hasImage = false
        var hasVideo = false
        mItems?.forEach {
            if (it.isImage() && !hasImage) hasImage = true
            if (it.isVideo() && !hasVideo) hasVideo = true
        }
        if (hasImage && hasVideo) {
            mCollectionType = COLLECTION_MIXED
        } else if (hasImage) {
            mCollectionType = COLLECTION_IMAGE
        } else if (hasVideo) {
            mCollectionType = COLLECTION_VIDEO
        }
    }

    /**
     * 同步已选 Bundle
     */
    fun syncSelectedMedias(@Nullable result: Bundle?) {
        val mMedias = result?.getParcelableArrayList<MediaEntity>(STATE_SELECTION)
        val mType = result?.getInt(STATE_COLLECTION_TYPE, COLLECTION_UNDEFINED)
        mCollectionType = if (mMedias?.size == 0) {
            COLLECTION_UNDEFINED
        } else {
            mType ?: COLLECTION_UNDEFINED
        }
        mItems?.clear()
        mMedias?.let { mItems?.addAll(it) }
    }

    /**
     * 检测媒体数据是否被选中
     */
    fun isSelected(media: MediaEntity) = mItems?.contains(media) ?: false

    /**
     * 检测已选媒体数据是否符合条件
     */
    fun checkSelectedMedia(media: MediaEntity): Boolean {
        val cause = isAcceptable(media)
        IncapableCause.handleCause(this, cause)
        return cause == null
    }

    private fun isAcceptable(media: MediaEntity): IncapableCause? {
        if (maxSelectableReached()) {
            val maxSelectable = currentMaxSelectable()
            val cause = try {
                getString(R.string.error_over_count, maxSelectable)
            } catch (e: Resources.NotFoundException) {
                getString(R.string.error_over_count, maxSelectable)
            } catch (e: NoClassDefFoundError) {
                getString(R.string.error_over_count, maxSelectable)
            }
            return IncapableCause(cause)
        } else if (typeConflict(media)) {
            return IncapableCause(getString(R.string.error_type_conflict))
        }
        return isAcceptable(this, media)
    }

    /**
     * @return 当前最大可选数量
     */
    private fun maxSelectableReached() = mItems?.size == currentMaxSelectable()

    private fun currentMaxSelectable(): Int {
        val selectionSpec = SelectionSpec.with()
        return when {
            selectionSpec.maxSelectable > 0 -> selectionSpec.maxSelectable
            mCollectionType == COLLECTION_IMAGE -> selectionSpec.maxImageSelectable
            mCollectionType == COLLECTION_VIDEO -> selectionSpec.maxVideoSelectable
            else -> selectionSpec.maxSelectable
        }
    }

    /**
     * Determine whether there will be conflict media types. A user can only select images and videos at the same time
     * while [SelectionSpec.mediaTypeExclusive] is set to false.
     */
    private fun typeConflict(media: MediaEntity): Boolean {
        return (SelectionSpec.with().mediaTypeExclusive
                && (media.isImage() && (mCollectionType == COLLECTION_VIDEO || mCollectionType == COLLECTION_MIXED)
                || media.isVideo() && (mCollectionType == COLLECTION_IMAGE || mCollectionType == COLLECTION_MIXED)))
    }

    private fun isAcceptable(context: Context, media: MediaEntity): IncapableCause? {
        if (!isSelectableType(context, media)) {
            return IncapableCause(context.getString(R.string.error_file_type))
        }
        SelectionSpec.with().filters.forEach {
            val incapableCause = it.filter(context, media)
            if (incapableCause != null) {
                return incapableCause
            }
        }
        return null
    }

    private fun isSelectableType(context: Context?, media: MediaEntity): Boolean {
        if (context == null) return false
        val resolver = context.contentResolver
        SelectionSpec.with().mimeTypeSet?.forEach {
            if (it.checkType(resolver, media.contentUri)) {
                return true
            }
        }
        return false
    }
}